package com.levy.swrb.core.component;


import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Assert;
import com.levy.swrb.core.config.SwrbProperties;
import com.levy.swrb.core.model.Node;
import com.levy.swrb.core.utils.NodeCalculateUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.*;
import org.redisson.client.codec.StringCodec;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
import org.springframework.util.FileCopyUtils;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.List;

import static com.levy.swrb.core.constants.Constant.*;

@EnableConfigurationProperties(SwrbProperties.class)
@RequiredArgsConstructor
@Slf4j
@Component
public class SmoothWeightedLoadBalancer implements InitializingBean {

    private final RedissonClient client;
    private final SwrbProperties properties;
    private final ResourceLoader resourceLoader;
    private String luaScript;
    private RList<String> rList;
    RScript script;

    public void saveNode(List<Node> nodeList) {
        checkList(nodeList);
        RLock lock = client.getLock(properties.getRedisPrefix() + SAVE_NODE_LOCK_KEY);
        if (!lock.tryLock()) {
            throw new RuntimeException("正在保存节点队列");
        }
        try {
            rList.clear();
            RAtomicLong rAtomicLong = client.getAtomicLong(properties.getRedisPrefix() + NODE_INDEX_KEY);
            rAtomicLong.set(0);
            List<String> idList = NodeCalculateUtil.calculateIdList(nodeList);
            rList.addAll(idList);
        } finally {
            lock.unlock();
        }
    }

    public String nextId() {
        if (rList.isEmpty()) {
            throw new RuntimeException("队列未初始化");
        }
        String indexKey = properties.getRedisPrefix() + NODE_INDEX_KEY;
        String nodeListKey = properties.getRedisPrefix() + NODE_LIST_KEY;
        return script.eval(RScript.Mode.READ_WRITE, luaScript, RScript.ReturnType.INTEGER,
                ListUtil.of(nodeListKey, indexKey));
    }


    @Override
    public void afterPropertiesSet() throws IOException {
        rList = client.getList(properties.getRedisPrefix() + NODE_LIST_KEY, StringCodec.INSTANCE);
        script = client.getScript(StringCodec.INSTANCE);
        Resource resource = resourceLoader.getResource("classpath:lua/nextId.lua");
        try (Reader reader = new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8)) {
            luaScript = FileCopyUtils.copyToString(reader);
        }
    }

    private void checkList(List<Node> nodeList) {
        Assert.notEmpty(nodeList);
        int maxSize = properties.getNodeListMaxSize();
        Assert.isTrue(nodeList.size() <= maxSize,
                String.format("节点队列最大长度: %s, 当前队列长度: %s", maxSize, nodeList.size()));
        int maxWeight = properties.getNodeMaxWeight();

        nodeList.forEach(node -> {
            Assert.notBlank(node.getId());
            Assert.notNull(node.getWeight());
            Assert.notNull(node.getCurrent());
            Assert.checkBetween(node.getWeight(), 0, maxWeight,
                    String.format("节点最大权重: %s, 异常节点: %s", maxWeight, node));
        });


    }

}
